package service

import (
	"fmt"
	ClickHouse "gin/database"
	"gin/model"
	"gin/types"
	"github.com/gin-gonic/gin"
	"gorm.io/gorm"
	"strconv"
	"strings"
	"time"
)

type TaskMyResponsible struct {
}

// GetList 我负责的列表
func (t *TaskMyResponsible) GetList(ctx *gin.Context, req types.GetListReq, tenantId int64) (res []types.GetListRes, err error) {
	current := time.Now().Unix()

	var rStepCategoryJoined = false
	var db *gorm.DB
	if req.DepartmentId > 0 || len(req.TodoStepCategoryId) > 0 { // 按组划分
		var departmentUserIdList []int64
		if req.DepartmentId > 0 {
			err = ClickHouse.DB.Model(&model.TenantUser{}).Joins("join user on user.union_id = tenant_user.user_id").
				Where("tenant_user.department_id = ?", req.DepartmentId).
				Select("distinct user.id uid").Pluck("uid", &departmentUserIdList).Error
			if err != nil || len(departmentUserIdList) == 0 {
				return res, err
			}
		}

		db = ClickHouse.DB.Model(&model.Task{}).
			Select("user.id as Id, count(distinct task.id) as TaskNum,task_member.partner,user.name as Name,"+
				"count((simpleJSONExtractInt(task.json, 'task_status')  IN (7,8)) or null) as FinalTaskNum,"+
				"floor(sum(toFloat64OrNull(task.actual_man_hour)),1) as FinalWorkTime,"+
				"sum(roat.rn) as RepairNum,"+
				"count(((task.step_type = 'step' and ("+
				"task.assign_status = 'default' "+
				"or simpleJSONExtractInt(task.json, 'task_status') in (select id from status where code in ('not_started','ready_to_start','wait_assemble'))) "+
				"and task.plan_end_time between 1 and ? and simpleJSONExtractInt(task.json, 'task_status') not in (3, 7)) "+
				"or (task.plan_end_time between 1 and ? and simpleJSONExtractInt(task.json, 'task_status') not in (3, 7)) "+
				"or task_work_order_temp.task_id > 0) or null)  AbnormalNum", current, current).
			Joins("join task_member on task.id = task_member.task_id").
			Joins("join user on user.id = task_member.partner").
			Joins("LEFT JOIN (select count(id) rn,task_id from repair_order_affected_task group by task_id) roat on task.id = roat.task_id").
			Joins("LEFT JOIN (select task_id from task_work_order where status = 0 group by task_id) task_work_order_temp on task_work_order_temp.task_id = task.id ").
			Where("task.tenant_id = ? ", tenantId).
			Where("task.step_type <> 'step_category' AND JSONLength(JSONExtractString(task.json, 'task_deprecate_data')) = 0 and simpleJSONExtractInt(task.json,'task_status') <> 3")
		if req.SearchName != "" {
			db.Where("user.name LIKE ?", fmt.Sprintf("%s%s%s", "%", req.SearchName, "%"))
		}
		if len(req.ExecutorUserIdList) > 0 {
			db.Where("task_member.partner in ?", req.ExecutorUserIdList)
		}
		if len(departmentUserIdList) > 0 {
			db.Where("task_member.partner in ?", departmentUserIdList)
		}
		db.Group("task_member.partner,user.id,user.name")
	} else { // 默认按部门划分

		db = ClickHouse.DB.Model(&model.Task{}).
			Select("d.id as Id, d.name as Name, count(distinct task.id) as TaskNum,"+
				"count((simpleJSONExtractInt(task.json, 'task_status') IN (7,8)) or null) as FinalTaskNum,"+
				"floor(sum(toFloat64OrNull(task.actual_man_hour)),1) as FinalWorkTime,"+
				"sum(roat.rn) as RepairNum,"+
				"count(((task.step_type = 'step' and ("+
				"task.assign_status = 'default' "+
				"or simpleJSONExtractInt(task.json, 'task_status') in (select id from status where code in ('not_started','ready_to_start','wait_assemble'))) "+
				"and task.plan_end_time between 1 and ? and simpleJSONExtractInt(task.json, 'task_status') not in (3, 7)) "+
				"or (task.plan_end_time between 1 and ? and simpleJSONExtractInt(task.json, 'task_status') not in (3, 7)) "+
				"or task_work_order_temp.task_id > 0) or null) AbnormalNum", current, current).
			Joins("JOIN task_member on task.id = task_member.task_id").
			Joins("JOIN user on user.id = task_member.partner").
			Joins("JOIN tenant_user on tenant_user.user_id = user.union_id").
			Joins("INNER JOIN department d on tenant_user.department_id = d.id").
			Joins("LEFT JOIN (select count(id) rn,task_id from repair_order_affected_task group by task_id) roat on task.id = roat.task_id").
			Joins("LEFT JOIN (select task_id from task_work_order where status = 0 group by task_id) task_work_order_temp on task_work_order_temp.task_id = task.id ").
			Where("task.tenant_id = ? and tenant_user.tenant_id = ?", tenantId, tenantId).
			Where("task.step_type <> 'step_category' AND JSONLength(JSONExtractString(task.json, 'task_deprecate_data')) = 0 and simpleJSONExtractInt(task.json,'task_status') <> 3")

		if req.SearchName != "" {
			db.Where("d.name LIKE ?", fmt.Sprintf("%s%s%s", "%", req.SearchName, "%"))
		}
		if req.SpecialRoleFilter != "" {
			var userService User
			userInfo, err := userService.GetMyUserInfo(ctx)
			if err != nil {
				return res, err
			}
			if req.SpecialRoleFilter == "artist" {
				var DepartmentService Department
				var departmentManagerDepartmentIdList []uint64
				departmentManagerDepartmentList, err := DepartmentService.getUserDepartmentListInfo(userInfo.Data.User.ID, userInfo.Data.Tenant.ID, []string{"headman", "step_producer"})
				for _, department := range departmentManagerDepartmentList {
					departmentManagerDepartmentIdList = append(departmentManagerDepartmentIdList, department.Id)
				}
				if len(departmentManagerDepartmentIdList) > 0 {
					//db.Where("task.department_id in ?", departmentManagerDepartmentIdList)
					var departmentUserIdList []int64
					err = ClickHouse.DB.Model(&model.TenantUser{}).Joins("join user on user.union_id = tenant_user.user_id").
						Where("tenant_user.department_id in ?", departmentManagerDepartmentIdList).
						Select("distinct user.id uid").Pluck("uid", &departmentUserIdList).Error
					if err != nil || len(departmentUserIdList) == 0 {
						return res, err
					}
					db.Where("task_member.partner in ? and d.id in ?", departmentUserIdList, departmentManagerDepartmentIdList)
				} else {
					return res, err
				}
			}
		}
		db.Group("d.id,d.name")
	}
	if req.DateType == "delivery" {
		db = db.Where("task.plan_delivery_time between ? and ?", req.StartTime, req.EndTime)
	} else if req.DateType == "delivery_actually" { // 实际交付筛选
		db = db.Where("task.delivery_time BETWEEN ? and ? and JSONExtractString(task.json,'task_status') = 7", req.StartTime, req.EndTime)
	} else {
		if req.StartTime > 0 && req.EndTime > 0 {
			db = db.Where("plan_end_time between ? and ?", req.StartTime, req.EndTime)
		}
	}

	if req.ProjectId > 0 {
		db.Where("task.project_id = ?", req.ProjectId)
	}
	if len(req.Page) > 0 {
		db.Offset((req.Page[0] - 1) * req.Page[1]).Limit(req.Page[1])
	}
	if len(req.TaskStepType) > 0 {
		db.Where("task.step_type in ?", req.TaskStepType)
	}
	if req.OnlyNotDone {
		db.Where("simpleJSONExtractInt(task.json, 'task_status') <> (select id from status where code = 'final')")
	}
	if len(req.TodoStepCategoryId) > 0 {
		if !rStepCategoryJoined {
			db = db.
				Joins("JOIN (select * from task_relation_tag where link_module_code in ('step_category','show_step_category')) r_step_category on r_step_category.task_id = task.id")
			rStepCategoryJoined = true
		}
		db = db.Where("r_step_category.link_id in ?", req.TodoStepCategoryId)
	}
	err = db.Find(&res).Error
	return
}

// GetCount 我负责统计
func (t *TaskMyResponsible) GetCount(ctx *gin.Context, req types.GetListReq) (res types.GetCountRes, err error) {
	req.IsMyResponsible = true

	if req.DepartmentId > 0 {
		err = ClickHouse.DB.Model(&model.TenantUser{}).
			Joins("join user on user.union_id = tenant_user.user_id").
			Where("tenant_user.department_id = ?", req.DepartmentId).
			Select("distinct user.id uid").Pluck("uid", &req.ExecutorUserIdList).Error
		if err != nil || len(req.ExecutorUserIdList) == 0 {
			return res, err
		}
		req.DepartmentId = 0

	} else {
		if len(req.TodoStepCategoryId) == 0 {
			var userService User
			userInfo, err := userService.GetMyUserInfo(ctx)
			if err != nil {
				return res, err
			}
			var departmentManagerDepartmentIdList []int64
			for _, v := range userInfo.Data.DepartmentInfo {
				if v.RoleCode == "headman" || v.RoleCode == "step_producer" {
					departmentManagerDepartmentIdList = append(departmentManagerDepartmentIdList, v.DepartmentID)
				}
			}

			err = ClickHouse.DB.Model(&model.TenantUser{}).Joins("join user on user.union_id = tenant_user.user_id").
				Where("tenant_user.department_id in ?", departmentManagerDepartmentIdList).
				Select("distinct user.id uid").Pluck("uid", &req.ExecutorUserIdList).Error
			if err != nil {
				return res, err
			}
		}
	}

	// todo
	ProductionManages := ProductionManage{}
	ToDayCountData, ToDayCountErr := ProductionManages.TodayCount(ctx, req.TodayCountReq)

	if ToDayCountErr != nil {
		return types.GetCountRes{}, err
	}

	var FinalRate float64 // 完成率
	if ToDayCountData.Total > 0 && ToDayCountData.Done > 0 {
		tmpNum := (float64(ToDayCountData.Done) / float64(ToDayCountData.Total)) * 100
		FinalRate, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", tmpNum), 64)
	}
	var RepairRate float64 // 返修率
	if ToDayCountData.Total > 0 && ToDayCountData.RepairNum > 0 {
		tmpNum := (float64(ToDayCountData.RepairNum) / float64(ToDayCountData.Total)) * 100
		RepairRate, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", tmpNum), 64)
	}

	res.FinalRate = FinalRate                    // 完成率
	res.RepairRate = RepairRate                  // 返修率
	res.RepairNum = ToDayCountData.RepairNum     // 返修
	res.TaskTotalNum = ToDayCountData.Total      // 任务总数
	res.TaskFinalNum = ToDayCountData.Done       // 已完成的
	res.ProgressNum = ToDayCountData.Inprogress  // 进行中的
	res.NotFinalNum = ToDayCountData.NotFinalNum // 未完成
	res.NotStartNum = ToDayCountData.NotStartNum // 未开始
	res.AbnormalNum = ToDayCountData.Abnormal    // 异常任务
	res.DelayFinalNum = 0                        // 暂时不做
	res.DelayRate = 0                            // 暂时不做

	return
}

// UserList 用户列表
func (t *TaskMyResponsible) UserList(req types.GetListReq, tenantId int64) (res []types.GetListRes, err error) {
	current := time.Now().Unix()

	var rStepCategoryJoined = false
	var db *gorm.DB

	db = ClickHouse.DB.Model(&model.Task{}).
		Select("user.id as Id, count(distinct task.id) as TaskNum,task_member.partner,user.name as Name,"+
			"count((simpleJSONExtractInt(task.json, 'task_status') IN (7,8)) or null) as FinalTaskNum,"+
			"floor(sum(toFloat64OrNull(task.actual_man_hour)),1) as FinalWorkTime,"+
			"sum(roat.rn) as RepairNum,"+
			"count(((task.step_type = 'step' and ("+
			"task.assign_status = 'default' "+
			"or simpleJSONExtractInt(task.json, 'task_status') in (select id from status where code in ('not_started','ready_to_start','wait_assemble'))) "+
			"and task.plan_end_time between 1 and ? and simpleJSONExtractInt(task.json, 'task_status') not in (3, 7)) "+
			"or (task.plan_end_time between 1 and ? and simpleJSONExtractInt(task.json, 'task_status') not in (3, 7)) "+
			"or task_work_order_temp.task_id > 0) or null)  AbnormalNum", current, current).
		Joins("join task_member on task.id = task_member.task_id").
		Joins("join user on user.id = task_member.partner").
		Joins("LEFT JOIN (select count(id) rn,task_id from repair_order_affected_task group by task_id) roat on task.id = roat.task_id").
		Joins("LEFT JOIN (select task_id from task_work_order where status = 0 group by task_id) task_work_order_temp on task_work_order_temp.task_id = task.id ").
		Where("task.tenant_id = ? ", tenantId).
		Where("task.step_type <> 'step_category' AND JSONLength(JSONExtractString(task.json, 'task_deprecate_data')) = 0 and simpleJSONExtractInt(task.json,'task_status') <> 3")

	var departmentUserIdList []int64
	if req.DepartmentId > 0 {
		err = ClickHouse.DB.Model(&model.TenantUser{}).Joins("join user on user.union_id = tenant_user.user_id").
			Where("tenant_user.department_id = ?", req.DepartmentId).
			Select("distinct user.id uid").Pluck("uid", &departmentUserIdList).Error
		if err != nil || len(departmentUserIdList) == 0 {
			return res, err
		}
	}

	if req.SearchName != "" {
		db.Where("user.name LIKE ?", fmt.Sprintf("%s%s%s", "%", req.SearchName, "%"))
	}
	if len(req.ExecutorUserIdList) > 0 {
		db.Where("task_member.partner in ?", req.ExecutorUserIdList)
	}
	db.Group("task_member.partner,user.id,user.name")
	if req.DateType == "delivery" {
		db.Where("task.plan_delivery_time between ? and ?", req.StartTime, req.EndTime)
	} else if req.DateType == "demand_add" { //新增
		db = db.Where("task.created BETWEEN ? and ? and JSONExtractString(task.json,'task_is_append') ='yes'", req.StartTime, req.EndTime)
	} else if req.DateType == "delay_created" { //延期
		var delayTaskIds []int64
		module := model.Module{}
		taskModule, err := module.GetByCode("task")
		if err != nil {
			return res, err
		}
		err = ClickHouse.DB.Model(&model.ReviewRequest{}).Where("module_id = ? and created BETWEEN ? and ?", taskModule.Id, req.StartTime, req.EndTime).Pluck("link_id", &delayTaskIds).Error
		if len(delayTaskIds) == 0 || err != nil {
			return res, err
		}

		db = db.Where("task.id in ?", delayTaskIds)
	} else if req.DateType == "delivery_actually" { // 实际交付筛选
		db = db.Where("task.delivery_time BETWEEN ? and ? and simpleJSONExtractInt(task.json,'task_status') = 7", req.StartTime, req.EndTime)
	} else {
		if req.StartTime > 0 && req.EndTime > 0 {
			db.Where("plan_end_time between ? and ?", req.StartTime, req.EndTime)
		}
	}

	if req.ProjectId > 0 {
		db.Where("task.project_id = ?", req.ProjectId)
	}
	if len(req.Page) > 0 {
		db.Offset((req.Page[0] - 1) * req.Page[1]).Limit(req.Page[1])
	}
	if len(req.TaskStepType) > 0 {
		db.Where("task.step_type in ?", req.TaskStepType)
	}
	if req.OnlyNotDone {
		db.Having("TaskNum > 0 and TaskNum > FinalTaskNum")
	}
	if len(req.TodoStepCategoryId) > 0 {

		if req.StandardOrOther == "yes" {
			stepCategoryIds := ""
			for _, i2 := range req.TodoStepCategoryId {
				stepCategoryIds += strconv.FormatInt(i2, 10) + ","
			}
			stepCategoryIds = strings.TrimRight(stepCategoryIds, ",")
			departmentUserIdList = []int64{}
			err = ClickHouse.DB.Model(&model.TenantUser{}).
				Joins("join user on user.union_id = tenant_user.user_id").
				Joins("join department on department.id = tenant_user.department_id").
				Where(fmt.Sprintf("hasAny(arrayMap(x->toInt64OrDefault(x),splitByChar(',',department.step_category_ids)),[%s])", stepCategoryIds)).
				Where("tenant_user.tenant_id = ?", tenantId).
				Select("distinct user.id uid").Pluck("uid", &departmentUserIdList).Error
			if err != nil || len(departmentUserIdList) == 0 {
				return res, err
			}
			req.TodoStepCategoryId = []int64{}
		} else {
			if !rStepCategoryJoined {
				db.
					Joins("JOIN (select * from task_relation_tag where link_module_code in ('step_category','show_step_category')) r_step_category on r_step_category.task_id = task.id")
				rStepCategoryJoined = true
			}
			db.Where("r_step_category.link_id in ?", req.TodoStepCategoryId)
		}
	}

	if len(departmentUserIdList) > 0 {
		db.Where("task_member.partner in ?", departmentUserIdList)
	}
	err = db.Find(&res).Error
	return
}

// getCountBaseQuery
func (t *TaskMyResponsible) getCountBaseQuery(req types.TaskCountReq,
	userInfo types.GetMyUserInfoRes, delayTaskIds []int64, departmentUserIdList []int64) (res *gorm.DB) {

	rStepCategoryJoined := false
	//rEpisodeJoined := false
	taskMemberJoined := false
	res = ClickHouse.DB.Model(&model.Task{})
	if userInfo.Data.Tenant.ID > 0 {
		res = res.Where("task.tenant_id = ?", userInfo.Data.Tenant.ID)
	}
	if len(req.TaskStepType) > 0 {
		res = res.Where("task.step_type in ?", req.TaskStepType)
	}

	if len(req.TodoStepCategoryId) > 0 {
		if !rStepCategoryJoined {
			res = res.
				Joins("JOIN (select * from task_relation_tag where link_module_code in ('step_category','show_step_category')) r_step_category on r_step_category.task_id = task.id")
			rStepCategoryJoined = true
		}
		res = res.Where("r_step_category.link_id in ?", req.TodoStepCategoryId)
	}

	if req.ProjectId > 0 {
		res.Where("task.project_id = ? ", req.ProjectId)
	}
	if len(req.ExecutorUserIdList) > 0 {
		if !taskMemberJoined {
			res = res.Joins("JOIN task_member on task_member.task_id = task.id")
			taskMemberJoined = true
		}
		res.Where("task_member.partner in ?", req.ExecutorUserIdList)
	}
	if len(departmentUserIdList) > 0 {
		if !taskMemberJoined {
			res = res.Joins("JOIN task_member on task_member.task_id = task.id")
			taskMemberJoined = true
		}
		res.Where("task_member.partner in ?", departmentUserIdList)
	}

	if req.DateType == "delivery" {
		res.Where("task.plan_delivery_time between ? and ?", req.StartTime, req.EndTime)
	} else if req.DateType == "demand_add" { //新增
		res = res.Where("task.created BETWEEN ? and ? and JSONExtractString(task.json,'task_is_append') ='yes'", req.StartTime, req.EndTime)
	} else if req.DateType == "delay_created" { //延期
		res = res.Where("task.id in ?", delayTaskIds)
	} else if req.DateType == "delivery_actually" { // 实际交付筛选
		res = res.Where("task.delivery_time BETWEEN ? and ? and simpleJSONExtractInt(task.json,'task_status') = 7", req.StartTime, req.EndTime)
	} else {
		res = res.Where("task.plan_end_time BETWEEN ? and ?", req.StartTime, req.EndTime)
	}
	if req.OnlyNotDone {
		res = res.Where("simpleJSONExtractInt(task.json, 'task_status') <> (select id from status where code = 'final')")
	}

	res = res.Where("task.step_type <> 'step_category'").
		Where("JSONLength(JSONExtractString(task.json, 'task_deprecate_data')) = 0 and simpleJSONExtractInt(task.json,'task_status') <> 3")
	return
}

// Count 统计
func (t *TaskMyResponsible) Count(ctx *gin.Context, req types.TaskCountReq, tenantId int64) (res types.TaskSimpleCountRes, err error) {
	// 部门id 处理成人员筛选
	var departmentUserIdList []int64

	if req.DepartmentId > 0 {
		err = ClickHouse.DB.Model(&model.TenantUser{}).
			Joins("join user on user.union_id = tenant_user.user_id").
			Where("tenant_user.department_id = ?", req.DepartmentId).
			Select("distinct user.id uid").Pluck("uid", &departmentUserIdList).Error
		if err != nil || len(departmentUserIdList) == 0 {
			return res, err
		}
		req.DepartmentId = 0
	}
	current := time.Now().Unix()
	var userService User
	userInfo, err := userService.GetMyUserInfo(ctx)
	if err != nil {
		return res, err
	}

	var taskIds []int64
	if req.DateType == "delay_created" {
		module := model.Module{}
		taskModule, err := module.GetByCode("task")
		if err != nil {
			return res, err
		}
		rd := ClickHouse.DB.Model(&model.ReviewRequest{}).
			Where("module_id = ? and created BETWEEN ? and ?", taskModule.Id, req.StartTime, req.EndTime).
			Distinct("link_id")
		if req.ProjectId > 0 {
			rd.Where("review_request.project_id = ?", req.ProjectId)
		}

		err = rd.Pluck("link_id", &taskIds).Error
		if len(taskIds) == 0 && err != nil {
			return res, err
		}
	}

	if req.StandardOrOther == "yes" && len(req.TodoStepCategoryId) > 0 {
		stepCategoryIds := ""
		for _, i2 := range req.TodoStepCategoryId {
			stepCategoryIds += strconv.FormatInt(i2, 10) + ","
		}
		stepCategoryIds = strings.TrimRight(stepCategoryIds, ",")
		departmentUserIdList = []int64{}
		err = ClickHouse.DB.Model(&model.TenantUser{}).
			Joins("join user on user.union_id = tenant_user.user_id").
			Joins("join department on department.id = tenant_user.department_id").
			Where(fmt.Sprintf("hasAny(arrayMap(x->toInt64OrDefault(x),splitByChar(',',department.step_category_ids)),[%s])", stepCategoryIds)).
			Where("tenant_user.tenant_id = ?", tenantId).
			Select("distinct user.id uid").Pluck("uid", &departmentUserIdList).Error
		if err != nil || len(departmentUserIdList) == 0 {
			return res, err
		}
		req.TodoStepCategoryId = []int64{}
	}

	err = t.getCountBaseQuery(req, userInfo, taskIds, departmentUserIdList).
		Pluck("count(distinct task.id) as Total", &res.Total).Error
	if err != nil {
		return
	}
	if req.DateType == "delivery" {
		err = t.getCountBaseQuery(req, userInfo, taskIds, departmentUserIdList).
			Where("simpleJSONExtractInt(task.json, 'task_status') IN (7)").
			Pluck("count(distinct task.id) as PlanDone", &res.Done).Error
	} else {
		err = t.getCountBaseQuery(req, userInfo, taskIds, departmentUserIdList).
			Where("simpleJSONExtractInt(task.json, 'task_status') IN (7,8)").
			Pluck("count(distinct task.id) as PlanDone", &res.Done).Error
	}
	if err != nil {
		return
	}
	err = t.getCountBaseQuery(req, userInfo, taskIds, departmentUserIdList).
		Joins("LEFT JOIN (select task_id from task_work_order where status = 0 group by task_id) task_work_order_temp on task_work_order_temp.task_id = task.id").
		Where("(task.step_type = 'step' and ("+
			"task.assign_status = 'default' "+
			"or simpleJSONExtractInt(task.json, 'task_status') in (select id from status where code in ('not_started','ready_to_start','wait_assemble'))) "+
			"and task.plan_end_time between 1 and ? and simpleJSONExtractInt(task.json, 'task_status') not in (3, 7)) "+
			"or (task.plan_end_time between 1 and ? and simpleJSONExtractInt(task.json, 'task_status') not in (3, 7)) "+
			"or task_work_order_temp.task_id > 0  ", current, current).
		Pluck("count(distinct task.id) as Abnormal", &res.Abnormal).Error
	return
}
